package com.sp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sp.exception.BusinessException;
import com.sp.mapper.EmployeeAndEmployeeRoleMapper;
import com.sp.mapper.EmployeeRoleAndMenuMapper;
import com.sp.mapper.EmployeeRoleAndPermissionMapper;
import com.sp.mapper.EmployeeRoleMapper;
import com.sp.model.dto.EmployeeRoleDTO;
import com.sp.model.entity.EmployeeRole;
import com.sp.model.entity.Menu;
import com.sp.model.entity.Permission;
import com.sp.service.EmployeeRoleService;
import com.sp.service.MenuService;
import com.sp.service.PermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.sp.constant.EmployeeRoleMessageConstant.*;
import static com.sp.constant.RedisConstant.*;

/**
 * 管理员角色服务实现类
 *
 * @author tong
 * @date 2024/3/25 14:51
 */
@Service
public class EmployeeRoleServiceImpl extends ServiceImpl<EmployeeRoleMapper, EmployeeRole> implements EmployeeRoleService {

    @Autowired
    private EmployeeAndEmployeeRoleMapper employeeAndEmployeeRoleMapper;

    @Autowired
    private EmployeeRoleAndPermissionMapper employeeRoleAndPermissionMapper;

    @Autowired
    private EmployeeRoleAndMenuMapper employeeRoleAndMenuMapper;

    @Autowired
    private PermissionService permissionService;

    @Autowired
    private MenuService menuService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Transactional
    @Override
    public boolean addEmployeeRole(EmployeeRoleDTO employeeRoleDTO) {
        this.inspectEmployeeRoleDTO(employeeRoleDTO, ADD_EMPLOYEE_ROLE_FAIL);

        // 删除管理员角色列表缓存
        stringRedisTemplate.delete(EMPLOYEE_ROLES);

        EmployeeRole employeeRole = BeanUtil.copyProperties(employeeRoleDTO, EmployeeRole.class);
        // 添加管理员角色
        this.save(employeeRole);
        // 添加管理员角色关联
        this.addAssociation(employeeRoleDTO, employeeRole.getId());

        return true;
    }

    /**
     * 添加管理员角色与具体权限、菜单的关联
     *
     * @param employeeRoleDTO
     * @param employeeRole
     */
    private void addAssociation(EmployeeRoleDTO employeeRoleDTO, Integer employeeRole) {
        if (!employeeRoleDTO.getNoPermission()) {
            List<Integer> permissionIds = employeeRoleDTO.getPermissionIds();
            if (employeeRoleAndPermissionMapper.add(employeeRole, permissionIds) != permissionIds.size()) {
                throw new BusinessException(ADD_EMPLOYEE_ROLE_FAIL + "：添加具体权限数量不匹配");
            }
        }

        List<Integer> menuIds = employeeRoleDTO.getMenuIds();
        if (employeeRoleAndMenuMapper.add(employeeRole, menuIds) != menuIds.size()) {
            throw new BusinessException(ADD_EMPLOYEE_ROLE_FAIL + "：添加界面数量不匹配");
        }
    }

    @Transactional
    @Override
    public boolean updateEmployeeRole(EmployeeRoleDTO employeeRoleDTO) {
        this.inspectEmployeeRoleDTO(employeeRoleDTO, UPDATE_EMPLOYEE_ROLE_FAIL);

        // 删除管理员角色列表缓存
        stringRedisTemplate.delete(EMPLOYEE_ROLES);

        // 删除原本关联
        Integer roleId = employeeRoleDTO.getId();
        employeeRoleAndPermissionMapper.deleteByRoleId(roleId);
        employeeRoleAndMenuMapper.deleteByRoleId(roleId);

        // 更新管理员角色
        this.updateById(BeanUtil.copyProperties(employeeRoleDTO, EmployeeRole.class));
        // 添加管理员角色关联
        this.addAssociation(employeeRoleDTO, roleId);

        // 更新成功，清除已有该角色的管理员登录缓存
        this.deleteEmployeeFromRedisByRoleId(roleId);
        // 删除与该管理员角色有关的菜单缓存
        this.deleteMenuFromRedisByRoleId(roleId);

        return true;
    }

    /**
     * 检查用于添加和更新的EmployeeRoleDTO是否合法
     *
     * @param employeeRoleDTO
     */
    private void inspectEmployeeRoleDTO(EmployeeRoleDTO employeeRoleDTO, String message) {
        if (employeeRoleDTO == null ||
                StrUtil.isBlank(employeeRoleDTO.getRole()) || StrUtil.isBlank(employeeRoleDTO.getKeyword()) ||
                employeeRoleDTO.getLevel() == null || employeeRoleDTO.getLevel() < 1 ||
                StrUtil.isBlank(employeeRoleDTO.getDescription()) ||
                employeeRoleDTO.getNoPermission() == null) {
            throw new BusinessException(message + "：" + PARAM_ERROR);
        }

        // 校验具体权限参数
        if (!employeeRoleDTO.getNoPermission()) {
            if (employeeRoleDTO.getPermissionIds() == null || employeeRoleDTO.getPermissionIds().isEmpty()) {
                throw new BusinessException(message + "：" + PARAM_ERROR);
            }

            // 有选择权限，判断具体权限是否存在
            List<Integer> permissionIds = employeeRoleDTO.getPermissionIds();

            Map<Integer, Permission> permissionsMap = permissionService.selectAllMap();

            if (permissionIds.stream().anyMatch(permissionId -> permissionsMap.get(permissionId) == null)) {
                throw new BusinessException(message + "：具体权限不存在");
            }
        }

        if (employeeRoleDTO.getMenuIds() == null || employeeRoleDTO.getMenuIds().isEmpty()) {
            throw new BusinessException(message + "：" + PARAM_ERROR);
        }

        // 判断界面菜单是否存在
        List<Integer> menuIds = employeeRoleDTO.getMenuIds();

        Map<Integer, Menu> menusMap = menuService.selectAllMap();

        if (menuIds.stream().anyMatch(menuId -> menusMap.get(menuId) == null)) {
            throw new BusinessException(message + "：界面菜单不存在");
        }
    }

    @Transactional
    @Override
    public boolean deleteEmployeeRoles(List<Integer> ids) {
        if (ids == null || ids.size() == 0) {
            throw new BusinessException(DELETE_EMPLOYEE_ROLE_FAIL + "：" + PARAM_ERROR);
        }

        // 判断权限角色是否还有关联管理员
        for (Integer id : ids) {
            if (employeeAndEmployeeRoleMapper.countByRoleId(id) > 0) {
                throw new BusinessException(DELETE_EMPLOYEE_ROLE_FAIL + "：权限角色存在关联");
            }
        }

        // 删除管理员角色列表缓存
        stringRedisTemplate.delete(EMPLOYEE_ROLES);

        employeeRoleAndPermissionMapper.deleteByRoleIds(ids);
        employeeRoleAndMenuMapper.deleteByRoleIds(ids);
        return this.removeByIds(ids);
    }

    @Override
    public IPage<EmployeeRole> page(Integer currentPage, Integer pageSize, EmployeeRoleDTO employeeRoleDTO) {
        if (currentPage == null) {
            currentPage = 1;
        }

        if (pageSize == null) {
            pageSize = 10;
        }

        //条件查询
        QueryWrapper<EmployeeRole> queryWrapper = new QueryWrapper<>();
        if (employeeRoleDTO != null) {
            queryWrapper.eq(employeeRoleDTO.getLevel() != null, "`er`.`level`", employeeRoleDTO.getLevel())
                    .like(StrUtil.isNotBlank(employeeRoleDTO.getKeyword()), "`er`.`keyword`", employeeRoleDTO.getKeyword())
                    .like(StrUtil.isNotBlank(employeeRoleDTO.getRole()), "`er`.`role`", employeeRoleDTO.getRole())
                    .like(StrUtil.isNotBlank(employeeRoleDTO.getDescription()), "`er`.`description`", employeeRoleDTO.getDescription())
                    .like(StrUtil.isNotBlank(employeeRoleDTO.getRole()), "`er`.`role`", employeeRoleDTO.getRole());
        }
        IPage<EmployeeRole> page = new Page<>(currentPage, pageSize);

        return baseMapper.page(page, queryWrapper);
    }

    @Override
    public List<EmployeeRole> selectAll() {
        return this.selectAllFromRedis();
    }

    /**
     * 从缓存获取所有管理员权限角色
     *
     * @return
     */
    private List<EmployeeRole> selectAllFromRedis() {
        String json = stringRedisTemplate.opsForValue().get(EMPLOYEE_ROLES);
        if (StrUtil.isNotBlank(json)) {
            return JSONObject.parseArray(json, EmployeeRole.class);
        }

        // 缓存不存在
        List<EmployeeRole> employeeRoles = baseMapper.selectAll();
        stringRedisTemplate.opsForValue().set(EMPLOYEE_ROLES, JSONObject.toJSONString(employeeRoles),
                EMPLOYEE_ROLES_TTL, TimeUnit.MINUTES);
        return employeeRoles;
    }

    @Override
    public Map<Integer, EmployeeRole> selectAllMap() {
        return this.selectAllMapFromRedis();
    }

    /**
     * 从缓存获取所有管理员权限角色 map
     *
     * @return
     */
    private Map<Integer, EmployeeRole> selectAllMapFromRedis() {
        return this.selectAllFromRedis()
                .stream().collect(Collectors.toMap(EmployeeRole::getId, Function.identity()));
    }

    /**
     * 删除与该管理员角色有关的已登录的管理员
     *
     * @param roleId
     */
    private void deleteEmployeeFromRedisByRoleId(Integer roleId) {
        // 查询与该角色关联的管理员username
        List<String> usernames = employeeAndEmployeeRoleMapper.selectEmployeeUsernameByRoleId(roleId);

        List<String> keys = usernames.stream().map(username -> EMPLOYEE_LOGIN_USERNAME + username)
                .collect(Collectors.toList());

        stringRedisTemplate.delete(keys);
    }

    /**
     * 删除与该管理员角色有关的菜单缓存
     *
     * @param roleId
     */
    private void deleteMenuFromRedisByRoleId(Integer roleId) {
        stringRedisTemplate.delete(EMPLOYEE_MENU_ROLE + roleId);
    }

}
